package cn.com.chengmandian.core.open.filters;

import cn.com.chengmandian.core.common.constants.ResponseCode;
import cn.com.chengmandian.core.common.utils.UrlUtil;
import cn.com.chengmandian.core.open.cache.AppCacheService;
import cn.com.chengmandian.core.open.constants.OpenTypeEnum;
import cn.com.chengmandian.core.open.pojo.AuthAgent;
import cn.com.chengmandian.core.open.pojo.AuthArea;
import cn.com.chengmandian.core.open.pojo.PortalApp;
import cn.com.chengmandian.core.open.properties.OpenProperties;
import cn.com.chengmandian.core.redis.cache.AgentCacheService;
import cn.com.chengmandian.core.web.pojo.AgentInfo;
import cn.com.chengmandian.core.web.pojo.AjaxResult;
import cn.com.chengmandian.core.web.utils.LocalMapUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;
import java.util.TreeMap;

@Slf4j
@Component
public class OpenAgentAuthFilter extends OncePerRequestFilter {
    private final static String openUrl = "/*/open/**";

    private final static String SIGN = "sign";

    private final static String ACCESS_TOKEN = "accessToken";

    private final static String AREA_ID = "areaId";

    private final static String APP_KEY = "appKey";

    private final static String TIMESTAMP = "timestamp";

    @Autowired
    private AppCacheService appCacheService;

    @Autowired
    private AgentCacheService agentCacheService;

    @Autowired
    private OpenProperties openProperties;

    // 403 权限不足
    private void responseError(HttpServletResponse response, Integer code, String message) {
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(JSONUtil.toJsonStr(AjaxResult.error(code, message)));
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestURI = request.getRequestURI();
        if (!UrlUtil.filterUrls(openUrl, requestURI)) {
            filterChain.doFilter(request, response);
            return;
        }
        if (openProperties.getType() != null && !openProperties.getType().equals(OpenTypeEnum.AGENT.name())) {
            filterChain.doFilter(request, response);
            return;
        }
        String sign = request.getParameter(SIGN);
        if (StrUtil.isBlank(sign)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "缺少sign参数");
            return;
        }
        String appKey = request.getParameter(APP_KEY);
        if (StrUtil.isBlank(appKey)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "缺少appKey参数");
            return;
        }
        String timestamp = request.getParameter(TIMESTAMP);
        if (StrUtil.isBlank(timestamp)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "缺少timestamp参数");
            return;
        }
        try {
            if (DateUtil.between(new Date(), new Date(new Long(timestamp) * 1000), DateUnit.SECOND, true) > 60 * 3) {
                responseError(response, ResponseCode.ACCESS_DENIED, "请求时间与服务器时间差异较大");
                return;
            }
        } catch (Exception e) {
            responseError(response, ResponseCode.ACCESS_DENIED, e.getMessage());
            return;
        }

        PortalApp portalApp = appCacheService.getApp(appKey);
        if (portalApp == null) {
            responseError(response, ResponseCode.ACCESS_DENIED, "不存在的appKey");
            return;
        }
        if (!portalApp.getIsEnabled().equals(1)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "应用不可用");
            return;
        }

        Enumeration<String> names = request.getParameterNames();
        Map<String, String> params = new TreeMap<>();
        while (names.hasMoreElements()) {
            String key = names.nextElement();
            if (!key.equals(SIGN) && !key.equals(ACCESS_TOKEN)) {
                params.put(key, request.getParameter(key));
            }
        }
        String signUrl = requestURI + "?" +  MapUtil.join(params, "&", "=");
        String signLocal = SecureUtil.md5(signUrl + portalApp.getAppSecret());
        if (!sign.equals(signLocal)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "签名错误");
            return;
        }
        String accessToken = request.getParameter(ACCESS_TOKEN);
        if (StrUtil.isBlank(accessToken)) {
            responseError(response, ResponseCode.ACCESS_DENIED, "缺少accessToken");
            return;
        }
        AuthAgent authAgent = appCacheService.getAgent(accessToken);
        if (authAgent == null) {
            responseError(response, ResponseCode.ACCESS_DENIED, "accessToken过期或不可用");
            return;
        }
        AgentInfo agentInfo = agentCacheService.getAgent(authAgent.getAgentId() + "");
        if (agentInfo == null) {
            responseError(response, ResponseCode.ACCESS_DENIED, "运营品牌不存在");
            return;
        }

        String areaId = request.getParameter(AREA_ID);
        if (StrUtil.isNotBlank(areaId)) {
            AuthArea authArea = appCacheService.getArea(portalApp.getId(), authAgent.getAgentId(), areaId);
            if (authArea == null) {
                responseError(response, ResponseCode.ACCESS_DENIED, "区域未授权");
                return;
            }
            agentInfo.setAreaId(Integer.parseInt(areaId));
        }
        LocalMapUtil.setAgentInfo(agentInfo);

        filterChain.doFilter(request, response);
    }
}
